#include <iostream>
#include "udpServer.hpp"
#include <memory>
#include <unordered_map>
#include <fstream>
#include <string>
#include <signal.h>
#include "onlineUser.hpp"
using namespace std;
using namespace myserver;

const string dictTxt = "./dict.txt";
unordered_map<string, string> mp;

static void Usage(string str)
{
    cerr << "\nUsage\n\t" << str << "local port" << endl;
}

bool cutString(string &line, string &key, string &val, string cutway)
{
    auto iter = line.find(cutway);
    if (iter == string::npos)
    {
        // 没找到
        return false;
    }
    key = line.substr(0, iter);
    val = line.substr(iter + cutway.size()); // 因为这里的cutway可以不只一个字符
    return true;
}

static void loadfile()
{
    // 把文件加载到内存中
    ifstream in(dictTxt, std::ios::binary);
    if (!in.is_open())
    {
        cerr << "open fail:" << dictTxt << endl;
        exit(3);
    }
    // 文件操作
    string line;
    string key, val;
    while (getline(in, line))
    {
        if (cutString(line, key, val, ":"))
        {
            // 往mp中放入
            mp[key] = val;
        }
    }
    in.close();
    cout << "dictload success" << endl;
}

void reload(int signo)
{
    (void)signo;
    loadfile();
}

static void degprint()
{
    for (auto &it : mp)
    {
        cout << it.first << " : " << it.second << endl;
    }
}

// demo1
void handlerMessage(int fd, string ip, uint16_t port, string message)
{
    // 这里主要处理的是翻译工作
    auto it = mp.find(message);
    string response_message;
    if (it == mp.end())
    {
        response_message = "unknown";
    }
    else
        response_message = it->second;
    // 开始返回
    struct sockaddr_in tmp;
    bzero(&tmp, sizeof(tmp));
    tmp.sin_family = AF_INET;
    tmp.sin_addr.s_addr = inet_addr(ip.c_str());
    tmp.sin_port = htons(port);
    sendto(fd, response_message.c_str(), sizeof(response_message), 0, (struct sockaddr *)&tmp, sizeof(tmp));
}

// demo2
void handlercommond(int fd, string clientip, uint16_t clientport, string commandline)
{
    // 应该要解析命令行+fork+exec
    // 这里使用popen接口
    if (commandline.find("rm") != string::npos || commandline.find("rmdir") != string::npos || commandline.find("mv") != string::npos)
    {
        cout << clientip.c_str() << "[" << clientport << "]"
             << "正在执行非法操作" << endl;
        return;
    }
    char buffer[1024];
    string response_message;
    FILE *pf = popen(commandline.c_str(), "r");
    if (pf == nullptr)
        response_message = commandline + "exec fail";
    // 从pf中读
    while (fgets(buffer, sizeof(buffer), pf))
    {
        response_message += buffer;
    }
    pclose(pf);
    // 开始返回
    struct sockaddr_in tmp;
    bzero(&tmp, sizeof(tmp));
    tmp.sin_family = AF_INET;
    tmp.sin_addr.s_addr = inet_addr(clientip.c_str());
    tmp.sin_port = htons(clientport);
    sendto(fd, response_message.c_str(), sizeof(response_message), 0, (struct sockaddr *)&tmp, sizeof(tmp));
}

onlineUser user;

// demo3
void routeMessage(int fd, string ip, uint16_t port, string message)
{
    // 判断在线,并允许其上线
    if (message == "online")
        user.addUser(ip, port);
    if (message == "offline")
        user.deleteUser(ip, port);
    if (user.isOnline(ip, port))
    {
        // 进行消息的路由
        user.broadcastMessage(fd, ip, port, message);
    }
    else
    {
        // 还没上线，发送提示信息
        struct sockaddr_in tmp;
        bzero(&tmp, sizeof(tmp));
        tmp.sin_family = AF_INET;
        tmp.sin_addr.s_addr = inet_addr(ip.c_str());
        tmp.sin_port = htons(port);
        string response_message = "你还没有上线，请输入online上线";
        sendto(fd, response_message.c_str(), response_message.size(), 0, (struct sockaddr *)&tmp, sizeof(tmp));
    }
}


int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(1);
    }
    uint16_t port = atoi(argv[1]);
    // signal(2,reload);
    // loadfile();
    // degprint();
    // unique_ptr<udpServer> user(new udpServer(handlerMessage ,port));
    // unique_ptr<udpServer> user(new udpServer(handlercommond ,port));
    unique_ptr<udpServer> user(new udpServer(routeMessage, port));

    user->initServer();
    user->start();
    return 0;
}